package com.mnemonic.domain;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

import javax.xml.bind.annotation.XmlRootElement;

import com.mnemonic.utils.DateUtils;
import com.mnemonic.utils.Ints;

/**
 * 游戏数据
 * 
 * @author lifeng
 * @date 2021/05/13
 */
@XmlRootElement
public class Game implements StorageAble {

    private static final long serialVersionUID = 1L;

    /**
     * 一次服务运行开启计数
     */
    private static AtomicInteger count = new AtomicInteger(1);

    private String id;
    private String name;
    private Integer turns = 0;
    private Integer mines = 0;
    private Level level;
    private List<Player> players;
    private int[][] mineMap;
    private Boolean inited = false;
    private Turn turn;

    public Turn getTurn() {
        return turn;
    }

    public void setTurn(Turn turn) {
        this.turn = turn;
    }

    public Integer getMines() {
        return mines;
    }

    public void setMines(Integer mines) {
        this.mines = mines;
    }

    public Integer getTurns() {
        return turns;
    }

    public void setTurns(Integer turns) {
        this.turns = turns;
    }

    public Boolean getInited() {
        return inited;
    }

    public void setInited(Boolean inited) {
        this.inited = inited;
    }

    public int[][] getMineMap() {
        return mineMap;
    }

    public void setMineMap(int[][] mineMap) {
        this.mineMap = mineMap;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Level getLevel() {
        return level;
    }

    public void setLevel(Level level) {
        this.level = level;
    }

    public List<Player> getPlayers() {
        return players;
    }

    public void setPlayers(List<Player> players) {
        this.players = players;
    }

    /**
     * 开启新一轮
     * 
     * @return
     */
    public Turn newTurn() {
        int index = 0;
        if (turns == null || turns < 0) {
            index = Ints.random(this.players.size());
            turns = index;
        } else {
            index = ++turns % this.players.size();
        }

        // 这一回合的玩家
        Player player = this.players.get(index);
        Turn bout = new Turn();
        bout.setPlayer(player);
        bout.setMaxops(this.level.getBoutOps());
        bout.setBout(this.turns / this.players.size() + this.turns % this.players.size());
        return bout;
    }

    /**
     * 00000 000 空白区域<br>
     * 00000 010 空白区域 - 打开<br>
     * 00000 100 空白区域 - 无雷插旗<br>
     * 00000 110 空白区域 - 问号<br>
     * 
     * 00000 001 雷（高5位可能有其他数据）<br>
     * 00000 011 雷（开了且触雷）<br>
     * 00000 101 雷（有雷插旗 ）<br>
     * 00000 111 雷（问号 ）<br>
     * 
     * 00001 001 雷（周围有1个类）<br>
     * 00010 001 雷（周围有2个类）<br>
     * 00011 001 雷（周围有3个类）<br>
     * 00100 001 雷（周围有4个类）<br>
     * 00101 001 雷（周围有5个类）<br>
     * 00110 001 雷（周围有6个类）<br>
     * 00111 001 雷（周围有7个类）<br>
     * 01000 001 雷（周围有8个类）<br>
     * 
     * 
     * 布雷的方法 <br>
     * 0b00000000 <br>
     * 0b00000001 用最后一位表示有雷或无雷 <br>
     * 0b00000110 用倒数第二和第三位表示格子的状态 00： 表示没有开 01： 开了 10： 插旗 <br>
     * 0b01111000第2、3、4、5位表示雷的数量 <br>
     */
    public synchronized void burnMine(int ix, int iy) {
        if (this.inited) {
            return;
        }
        this.inited = true;

        // 第一点击的区域设置为无雷且打开
        mineMap[ix][iy] = 0b00000000;

        // 布雷
        Random rd = new Random();
        int mineCount = this.level.getMine();
        int rows = this.level.getRow();
        int cols = this.level.getCol();
        int x, y;
        for (int i = 0; i < mineCount;) {
            x = rd.nextInt(rows);
            y = rd.nextInt(cols);

            // 跳过第一次点击的格子
            if (x == ix && y == iy) {
                continue;
            }

            // 将这个位置周围的雷的数量加一
            // 说明没有雷
            if ((mineMap[x][y] & 0b00000001) == 0b00000000) {

                // 将雷放到这个位置
                mineMap[x][y] = 0b00000001;

                // 正上方
                if (x - 1 >= 0) {
                    mineMap[x - 1][y] += 0b000001000;
                }

                // 上面的左边
                if (x - 1 >= 0 && y - 1 >= 0) {
                    mineMap[x - 1][y - 1] += 0b000001000;
                }

                // 上面的右边
                if (x - 1 >= 0 && y + 1 < cols) {
                    mineMap[x - 1][y + 1] += 0b000001000;
                }

                // 正下方
                if (x + 1 < rows) {
                    mineMap[x + 1][y] += 0b000001000;
                }

                // 下面的左边
                if (x + 1 < rows && y - 1 >= 0) {
                    mineMap[x + 1][y - 1] += 0b000001000;
                }

                // 下面的右边
                if (x + 1 < rows && y + 1 < cols) {
                    mineMap[x + 1][y + 1] += 0b000001000;
                }

                // 左
                if (y - 1 >= 0) {
                    mineMap[x][y - 1] += 0b000001000;
                }

                // 右
                if (y + 1 < cols) {
                    mineMap[x][y + 1] += 0b000001000;
                }
                ++i;
            }
        }
    }

    /**
     * 重新开启一场游戏, 创建 level 和 player 的副本，保证在一场游戏中的数据不会覆盖全局数据
     * 
     * @param level
     * @param players
     * @return
     */
    public static Game newGame(Level level, List<Player> players) {
        Game game = new Game();
        game.setId(UUID.randomUUID().toString());
        game.setName(DateUtils.getTodayStr("yyyy年MM月dd日") + "第" + count.getAndIncrement() + "场");
        try {
            game.setLevel(level.clone());
            List<Player> copys = new ArrayList<>();
            for (Player player : players) {
                copys.add(player.clone());
            }
            game.setPlayers(copys);
        } catch (Exception e) {
        }

        // 创建雷区 -- 默认会初始化为0
        game.setMineMap(new int[level.getRow()][level.getCol()]);
        return game;
    }
}
